package com.mojian.service.impl;

import cn.dev33.satoken.stp.StpUtil;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.mojian.common.Constants;
import com.mojian.dto.article.ArticleQueryDto;
import com.mojian.entity.SysArticle;
import com.mojian.entity.SysTag;
import com.mojian.exception.ServiceException;
import com.mojian.mapper.SysArticleMapper;
import com.mojian.mapper.SysTagMapper;
import com.mojian.service.QCloudUploadStrategyImpl;
import com.mojian.service.SysArticleService;
import com.mojian.utils.PageUtils;
import com.mojian.vo.article.ArticleListVo;
import com.mojian.vo.article.SysArticleDetailVo;
import com.mojian.vo.tag.TagListVo;
import com.vladsch.flexmark.html2md.converter.FlexmarkHtmlConverter;
import com.vladsch.flexmark.util.data.MutableDataSet;
import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

@Service
@RequiredArgsConstructor
public class SysArticleServiceImpl extends ServiceImpl<SysArticleMapper, SysArticle> implements SysArticleService {

    private final SysTagMapper sysTagMapper;

    @Resource
    private QCloudUploadStrategyImpl qCloudUploadStrategy;

    @Override
    public IPage<ArticleListVo> selectPage(ArticleQueryDto articleQueryDto) {
        IPage<ArticleListVo> page = baseMapper.selectPageList(PageUtils.getPage(), articleQueryDto);
        for (ArticleListVo record : page.getRecords()) {
            List<TagListVo> tags = sysTagMapper.getTagByArticleId(record.getId());
            record.setTags(tags);
        }
        return page;
    }

    @Override
    public SysArticleDetailVo detail(Integer id) {
        SysArticle sysArticle = baseMapper.selectById(id);

        SysArticleDetailVo sysArticleDetailVo = new SysArticleDetailVo();
        BeanUtils.copyProperties(sysArticle, sysArticleDetailVo);

        //获取标签
        List<Integer> tags = sysTagMapper.getTagIdsByArticleId(id);
        sysArticleDetailVo.setTagIds(tags);
        return sysArticleDetailVo;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean add(SysArticleDetailVo sysArticle) {

        SysArticle obj = new SysArticle();
        BeanUtils.copyProperties(sysArticle, obj);

        obj.setUserId(StpUtil.getLoginIdAsInt());
        baseMapper.insert(obj);

        //添加标签
        sysTagMapper.addArticleTagRelations(obj.getId(), sysArticle.getTagIds());
        return true;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean update(SysArticleDetailVo sysArticle) {

        SysArticle obj = new SysArticle();
        BeanUtils.copyProperties(sysArticle, obj);

        baseMapper.updateById(obj);

        //先删除标签在新增标签
        sysTagMapper.deleteArticleTagsByArticleIds(Collections.singletonList(obj.getId()));
        //添加标签
        sysTagMapper.addArticleTagRelations(obj.getId(), sysArticle.getTagIds());
        return true;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean delete(List<Long> ids) {
        baseMapper.deleteBatchIds(ids);
        sysTagMapper.deleteArticleTagsByArticleIds(ids);
        return true;
    }

    @Override
    public void reptile(String url) {
        try {
            Document document = Jsoup.connect(url).get();
            Elements title = document.getElementsByClass("title-article");
            Elements tags = document.getElementsByClass("tag-link");
            Elements content = document.getElementsByClass("article_content");
            if (StringUtils.isBlank(content.toString())) {
                throw new ServiceException("爬取文章失败");
            }

            Elements imgElements = content.select("img");
            for (Element img : imgElements) {
                String imgUrl = img.attr("src");
                if (StringUtils.isNotBlank(imgUrl)) {
                    imgUrl = cleanImageUrl(imgUrl);
                    File tempFile = downloadImage(imgUrl);
                    if (tempFile!= null) {
                        String newUrl = qCloudUploadStrategy.upload(tempFile);
                        img.attr("src", newUrl);
                        tempFile.delete();
                    }
                }
            }

            String newContent = content.get(0).toString().replaceAll("<code>", "<code class=\"lang-java\">");
            String markdown = convertAnchors(FlexmarkHtmlConverter.builder(new MutableDataSet()).build().convert(reorderLanguageClass(newContent)));

            SysArticle entity = SysArticle.builder().userId(StpUtil.getLoginIdAsInt()).contentMd(markdown)
                    .isOriginal(Constants.NO).originalUrl(url)
                    .title(title.get(0).text()).cover("https://api.btstu.cn/sjbz/api.php?lx = dongman&format = images").content(newContent).build();

            baseMapper.insert(entity);
            List<Integer> tagIds = new ArrayList<>();
            tags.forEach(item -> {
                String tag = item.text();
                SysTag result = sysTagMapper.selectOne(new LambdaQueryWrapper<SysTag>().eq(SysTag::getName, tag));
                if (result == null) {
                    result = SysTag.builder().name(tag).build();
                    sysTagMapper.insert(result);
                }
                tagIds.add(result.getId());
            });
            sysTagMapper.addArticleTagRelations(entity.getId(), tagIds);

            System.out.println("文章抓取成功，内容为:" + JSON.toJSONString(entity));
        } catch (IOException e) {
            throw new ServiceException(e.getMessage());
        }
    }

    private String reorderLanguageClass(String input) {
        Pattern pattern = Pattern.compile("<code\\s+(class\\s*=\\s*\"([^\"]*)\")", Pattern.CASE_INSENSITIVE);
        Matcher matcher = pattern.matcher(input);
        StringBuffer result = new StringBuffer();
        while (matcher.find()) {
            String classAttr = matcher.group(2);
            int langIndex = classAttr.indexOf("language-");
            if (langIndex!= -1) {
                langIndex += "language-".length();
                int endIndex = classAttr.indexOf(' ', langIndex);
                if (endIndex == -1) {
                    endIndex = classAttr.length();
                }
                String lang = classAttr.substring(langIndex, endIndex);
                matcher.appendReplacement(result, "<code class=\"" + lang + "\"");
            } else {
                matcher.appendReplacement(result, matcher.group());
            }
        }
        matcher.appendTail(result);
        return result.toString();
    }

    private String convertAnchors(String input) {
        Pattern pattern = Pattern.compile("\\{#([^}]*)\\}");
        Matcher matcher = pattern.matcher(input);
        StringBuffer result = new StringBuffer();
        while (matcher.find()) {
            String id = matcher.group(1);
            matcher.appendReplacement(result, "<p id=\"" + id + "\"></p>");
        }
        matcher.appendTail(result);
        return result.toString();
    }

    private String cleanImageUrl(String url) {
        Pattern pattern = Pattern.compile("([?#].*)");
        Matcher matcher = pattern.matcher(url);
        return matcher.replaceAll("");
    }

    private File downloadImage(String imgUrl) {
        try {
            URL url = new URL(imgUrl);
            File tempFile = File.createTempFile("img", "." + imgUrl.substring(imgUrl.lastIndexOf('.') + 1));
            Files.copy(url.openStream(), Paths.get(tempFile.getAbsolutePath()), StandardCopyOption.REPLACE_EXISTING);
            return tempFile;
        } catch (IOException e) {
            e.printStackTrace();
            return null;
        }
    }
}
